package de.mmis.core.base.abstracttree.serializer.internal;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

import de.mmis.core.base.DeserializableAnnotations.DeserializationMethod;
import de.mmis.core.base.abstracttree.InnerNode;
import de.mmis.core.base.abstracttree.LeafNode;
import de.mmis.core.base.abstracttree.Tree;
import de.mmis.core.base.abstracttree.serializer.DeserializableException;
import de.mmis.core.base.abstracttree.serializer.SerializableNetworkInvocationHandler;
import de.mmis.core.base.abstracttree.serializer.Serializer;
import de.mmis.core.base.abstracttree.serializer.SerializerHelper;

/**
 * Serializer class for (de)serializing Proxy objects from/to Trees
 * 
 * @author Christian Koch
 */
public class ProxySerializer implements Serializer<Proxy> {

	@DeserializationMethod
	public static Object deserialize(Tree[] exps, ClassLoader classLoader,
			String id, Map<String, Object> idmap)
			throws DeserializableException {

		if (exps.length != 5)
			throw new DeserializableException(new InnerNode(exps),
					"Proxy Tree needs 5 parameters");

		String objID = exps[0].getAs(String.class, classLoader);
		Class<?> proxyClass = exps[1].getAs(Class.class, classLoader);
		Class<?>[] interfaces = exps[2].getAs(Class[].class, classLoader);
		String host = exps[3].getAs(String.class, classLoader);
		Integer port = exps[4].getAs(int.class, classLoader);

		Method m;
		try {
			m = proxyClass.getMethod("createProxy", String.class,
					ClassLoader.class, Class[].class, String.class, int.class);
		} catch (SecurityException e) {
			throw new DeserializableException(new InnerNode(exps), e,
					"error while getting proxy creation method");
		} catch (NoSuchMethodException e) {
			throw new DeserializableException(new InnerNode(exps), e,
					"error while getting proxy creation method");
		}

		Object ret;
		try {
			ret = m.invoke(null, objID, classLoader, interfaces, host, port);
		} catch (IllegalArgumentException e) {
			throw new DeserializableException(new InnerNode(exps), e,
					"error while invoking proxy creation method");
		} catch (IllegalAccessException e) {
			throw new DeserializableException(new InnerNode(exps), e,
					"error while invoking proxy creation method");
		} catch (InvocationTargetException e) {
			throw new DeserializableException(new InnerNode(exps), e,
					"error while invoking proxy creation method");
		}

		return ret;
	}

	@Override
	public Tree serialize(Proxy object, boolean explicit,
			Map<Object, String> refmap, ClassLoader classLoader) {

		InvocationHandler inv = Proxy.getInvocationHandler(object);
		if (!(inv instanceof SerializableNetworkInvocationHandler))
			throw new IllegalArgumentException("The passed object (" + object
					+ ") is not a serializable proxy object");
		SerializableNetworkInvocationHandler sinv = (SerializableNetworkInvocationHandler) inv;

		return new InnerNode(new LeafNode(SerializerHelper.typeIdentifierPrefix
				+ "proxy"), Tree.fromObject(sinv.getID(), explicit, refmap,
				classLoader), Tree.fromObject(sinv.getProxyCreationClass(),
				explicit, refmap, classLoader),
				Tree.fromObject(sinv.getImplementingInterfaces(), explicit,
						refmap, classLoader), Tree.fromObject(sinv.getHost(),
						explicit, refmap, classLoader), Tree.fromObject(
						new Integer(sinv.getPort()), explicit, refmap,
						classLoader));
	}
}
